export class TaskQueue<T = any> {
  private concurrentLimit: number;
  private running = 0;
  private queue: Array<{ task: () => Promise<T>; index: number }> = [];
  private results: Array<{ index: number; value: T }> = [];
  private errors: Array<{ index: number; reason: T }> = [];
  private resolveStart: ((value: unknown) => void) | null = null;
  private started = false;

  constructor(concurrentLimit: number) {
    this.concurrentLimit = concurrentLimit;
  }

  public push(task: () => Promise<T>) {
    const index = this.queue.length;
    this.queue.push({ task, index });
  }

  public start(): Promise<{
    results: Array<{ index: number; value: T }>;
    errors: Array<{ index: number; reason: T }>;
  }> {
    if (this.started) {
      throw new Error('TaskQueue already started');
    }
    this.started = true;

    for (let i = 0; i < this.concurrentLimit; i++) {
      this.next();
    }
    return new Promise(resolve => {
      this.resolveStart = resolve;
    });
  }

  /**
   * 根据设置的 concurrentLimit 并发数，一次性启动多个任务并发执行。
   * 所有任务会尽可能并行执行，直到所有任务完成。
   * 适用于希望尽快完成所有任务且不关心任务执行顺序的场景。
   */
  private next() {
    if (this.queue.length === 0 && this.running === 0) {
      this.resolveStart?.({ results: this.results, errors: this.errors });
      this.resetState();
      return;
    }

    if (this.running >= this.concurrentLimit || this.queue.length === 0) {
      return;
    }

    const { task, index } = this.queue.shift()!;
    this.running++;

    Promise.resolve()
      .then(task)
      .then(result => {
        this.results[index] = { index, value: result };
      })
      .catch(error => {
        this.errors[index] = { index, reason: error };
      })
      .finally(() => {
        this.running--;
        this.next();
      });
  }

  private resetState() {
    this.running = 0;
    this.started = false;
    this.queue = [];
    this.results = [];
    this.errors = [];
    this.resolveStart = null;
  }

  protected arrayChunk<T>(array: T[], size: number): T[][] {
    return Array.from({ length: Math.ceil(array.length / size) }, (_, index) =>
      array.slice(index * size, (index + 1) * size),
    );
  }

  /**
   * seqStart 方法会将任务队列分成若干组，每组的任务数量不超过并发限制（concurrentLimit）。
   * 任务会按组顺序执行，组内任务并行，组间任务串行。
   * 适用于需要控制任务分组执行顺序的场景，或者希望在某些任务组完成后再开始下一组任务的场景。
   */
  public async seqStart(): Promise<{
    results: Array<{ index: number; value: T }>;
    errors: Array<{ index: number; reason: T }>;
  }> {
    if (this.queue.length <= this.concurrentLimit) {
      return this.start();
    }

    // eslint-disable-next-line no-async-promise-executor
    return new Promise(async resolve => {
      const chunkedQueue = this.arrayChunk(this.queue, this.concurrentLimit);
      for (let i = 0; i < chunkedQueue.length; i++) {
        const chunk = chunkedQueue[i];
        await Promise.all(
          chunk.map(({ task, index }) => {
            return Promise.resolve()
              .then(task)
              .then(value => {
                this.results[index] = { index, value };
              })
              .catch(reason => {
                this.errors[index] = { index, reason };
              });
          }),
        );
      }
      resolve({ results: this.results, errors: this.errors });
      this.resetState();
    });
  }
}
